// Copyright 2016 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_DECODER_H_
#define DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_DECODER_H_

#include "draco/compression/attributes/attributes_decoder_interface.h"
#include "draco/compression/config/compression_shared.h"
#include "draco/compression/config/decoder_options.h"
#include "draco/core/status.h"
#include "draco/point_cloud/point_cloud.h"

namespace draco {

// Abstract base class for all point cloud and mesh decoders. It provides a
// basic functionality that is shared between different decoders.
class PointCloudDecoder {
  public:
    PointCloudDecoder();
    virtual ~PointCloudDecoder() = default;

    virtual EncodedGeometryType GetGeometryType() const {
        return POINT_CLOUD;
    }

    // Decodes a Draco header int other provided |out_header|.
    // Returns false on error.
    static Status DecodeHeader(DecoderBuffer *buffer, DracoHeader *out_header);

    // The main entry point for point cloud decoding.
    Status Decode(const DecoderOptions &options, DecoderBuffer *in_buffer,
                  PointCloud *out_point_cloud);

    bool SetAttributesDecoder(
        int att_decoder_id, std::unique_ptr<AttributesDecoderInterface> decoder) {
        if (att_decoder_id < 0)
            return false;
        if (att_decoder_id >= static_cast<int>(attributes_decoders_.size()))
            attributes_decoders_.resize(att_decoder_id + 1);
        attributes_decoders_[att_decoder_id] = std::move(decoder);
        return true;
    }

    // Returns an attribute containing decoded data in their portable form that
    // is guaranteed to be the same for both encoder and decoder. I.e., it returns
    // an attribute before it was transformed back into its final form which may
    // be slightly different (non-portable) across platforms. For example, for
    // attributes encoded with quantization, this method returns an attribute
    // that contains the quantized values (before the dequantization step).
    const PointAttribute *GetPortableAttribute(int32_t point_attribute_id);

    uint16_t bitstream_version() const {
        return DRACO_BITSTREAM_VERSION(version_major_, version_minor_);
    }

    const AttributesDecoderInterface *attributes_decoder(int dec_id) {
        return attributes_decoders_[dec_id].get();
    }
    int32_t num_attributes_decoders() const {
        return attributes_decoders_.size();
    }

    // Get a mutable pointer to the decoded point cloud. This is intended to be
    // used mostly by other decoder subsystems.
    PointCloud *point_cloud() {
        return point_cloud_;
    }
    const PointCloud *point_cloud() const {
        return point_cloud_;
    }

    DecoderBuffer *buffer() {
        return buffer_;
    }
    const DecoderOptions *options() const {
        return options_;
    }

  protected:
    // Can be implemented by derived classes to perform any custom initialization
    // of the decoder. Called in the Decode() method.
    virtual bool InitializeDecoder() {
        return true;
    }

    // Creates an attribute decoder.
    virtual bool CreateAttributesDecoder(int32_t att_decoder_id) = 0;
    virtual bool DecodeGeometryData() {
        return true;
    }
    virtual bool DecodePointAttributes();

    virtual bool DecodeAllAttributes();
    virtual bool OnAttributesDecoded() {
        return true;
    }

    Status DecodeMetadata();

  private:
    // Point cloud that is being filled in by the decoder.
    PointCloud *point_cloud_;

    std::vector<std::unique_ptr<AttributesDecoderInterface>> attributes_decoders_;

    // Map between attribute id and decoder id.
    std::vector<int32_t> attribute_to_decoder_map_;

    // Input buffer holding the encoded data.
    DecoderBuffer *buffer_;

    // Bit-stream version of the encoder that encoded the input data.
    uint8_t version_major_;
    uint8_t version_minor_;

    const DecoderOptions *options_;
};

}  // namespace draco

#endif  // DRACO_COMPRESSION_POINT_CLOUD_POINT_CLOUD_DECODER_H_
